﻿using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System;

public class FM_Chord : FM_BaseNote
{
    public List<FM_BaseNote> Notes;

    public FM_Chord(FM_ScoreBase score) : base(FM_NoteType.CHORD, score)
    {
        Notes = new List<FM_BaseNote>();
        duration = FM_DurationValue.NOTE_WHOLE;
    }

    public void AddNote(FM_BaseNote note)
    {
        Notes.Add(note);
        if (FM_Const.GetDurationMs(note.duration) > FM_Const.GetDurationMs(duration))
            duration = note.duration;
    }

    public void Compute()
    {
        FM_BaseNote tmp;

        // Sort the notes in the chord by staff and displacement
        for (int i = 0; i < Notes.Count - 1; i++)
        {
            for (int j = i + 1; j < Notes.Count; j++)
            {
                if ((Notes[i].stave > Notes[j].stave) || (Notes[i].stave == Notes[j].stave && Notes[i].GetDisplacement() <= Notes[j].GetDisplacement()))
                {
                    tmp = Notes[i];
                    Notes[i] = Notes[j];
                    Notes[j] = tmp;
                }
            }
        }

        // Remove accidental and/or dot from the second note if the distance between any two notes is 0
        for (int i = 0; i < Notes.Count; i++)
        {
            for (int j = i + 1; j < Notes.Count; j++)
            {
                int distance = Math.Abs(FM_Const.DistanceBetweenNotes(Notes[i], Notes[j]));
                if (distance == 0)
                {
                    Notes[j].RemoveAccidental();
                    if (Notes[i].duration == Notes[j].duration && (int)Notes[i].duration > 50)
                    {
                        Notes[i].displacedot = 1;
                        Notes[j].displacedot = -1;
                        Notes[j].duration = Notes[j].duration;
                    }
                }
            }
        }

        // Get the maximum width without the DOT
        float maxW = 0;
        float w;
        for (int i = 0; i < Notes.Count; i++)
        {
            w = Notes[i].WidthNoDotNoStem() - Notes[i].WidthNoteNoStem() / 2f;
            if (w > maxW) maxW = w;
        }

        // Pad the notes to have the same width, without the DOT (aligning them)
        for (int i = 0; i < Notes.Count; i++)
        {
            w = Notes[i].WidthNoDotNoStem() - Notes[i].WidthNoteNoStem() / 2f;
            Notes[i].SetPaddingLeft(maxW - w);
        }

        for (int i = 0; i < Notes.Count; i++)
        {
            for (int j = i + 1; j < Notes.Count; j++)
            {
                int distance = Math.Abs(FM_Const.DistanceBetweenNotes(Notes[i], Notes[j]));

                // Step 5
                // if the distance between the notes is 0, line them up
                if (distance == 0)
                {
                    int ni = 0;
                    int nj = 0;

                    if (Notes[i].duration == FM_DurationValue.NOTE_WHOLE || Notes[i].duration == FM_DurationValue.NOTE_WHOLE_D) ni = 1;
                    if (Notes[i].duration == FM_DurationValue.NOTE_HALF || Notes[i].duration == FM_DurationValue.NOTE_HALF_D) ni = 2;
                    if (Notes[i].duration == FM_DurationValue.NOTE_QUARTER || Notes[i].duration == FM_DurationValue.NOTE_EIGHTH || Notes[i].duration == FM_DurationValue.NOTE_SIXTEENTH || Notes[i].duration == FM_DurationValue.NOTE_THIRTY_SECOND) ni = 3;
                    if (Notes[i].duration == FM_DurationValue.NOTE_QUARTER_D || Notes[i].duration == FM_DurationValue.NOTE_EIGHTH_D || Notes[i].duration == FM_DurationValue.NOTE_SIXTEENTH_D || Notes[i].duration == FM_DurationValue.NOTE_THIRTY_SECOND_D) ni = 4;

                    if (Notes[j].duration == FM_DurationValue.NOTE_WHOLE || Notes[j].duration == FM_DurationValue.NOTE_WHOLE_D) nj = 1;
                    if (Notes[j].duration == FM_DurationValue.NOTE_HALF || Notes[j].duration == FM_DurationValue.NOTE_HALF_D) nj = 2;
                    if (Notes[j].duration == FM_DurationValue.NOTE_QUARTER || Notes[j].duration == FM_DurationValue.NOTE_EIGHTH || Notes[j].duration == FM_DurationValue.NOTE_SIXTEENTH || Notes[j].duration == FM_DurationValue.NOTE_THIRTY_SECOND) nj = 3;
                    if (Notes[j].duration == FM_DurationValue.NOTE_QUARTER_D || Notes[j].duration == FM_DurationValue.NOTE_EIGHTH_D || Notes[j].duration == FM_DurationValue.NOTE_SIXTEENTH_D || Notes[j].duration == FM_DurationValue.NOTE_THIRTY_SECOND_D) nj = 4;

                    if (ni == 1 || ni != nj)
                    {
                        float allWidth = (Notes[i].WidthNote() + Notes[j].WidthNote()) / 2f;

                        if (Notes[i].duration < Notes[j].duration)
                        {
                            // the note with the higher duration (or with a dot) should be to the right
                            allWidth = allWidth * 0.5f;
                        }
                        else
                        {
                            allWidth = -allWidth * 0.5f;
                        }

                        // pad the dot on the first note
                        Notes[i].SetPaddingLeft(Notes[i].GetPaddingLeft() - allWidth);
                        // pad the note on the second note
                        Notes[j].SetPaddingLeft(Notes[j].GetPaddingLeft() + allWidth);

                        if (Notes[i].stem_up == Notes[j].stem_up) Notes[j].stem_up = !Notes[j].stem_up;
                    }
                }

                // Step 6
                // if the distance between the notes is 1, displace one of the notes
                if (distance == 1)
                {
                    // pad the dot on the first note
                    Notes[i].SetPaddingDot(Notes[i].paddingDot + Notes[j].WidthNote() * 0.8f);
                    // pad the note on the second note
                    Notes[j].SetPaddingNote(Notes[j].paddingNote + Notes[j].WidthNote() * 0.8f);

                    if (Notes[i].stem_up == Notes[j].stem_up) Notes[j].stem_up = !Notes[j].stem_up;
                }

                // Step 7
                // pad the accidentals when the distance between notes is less or equal to 3
                if (Notes[i].accidental != 0 && Notes[j].accidental != 0 && distance <= 3 && distance != 0)
                {
                    Notes[j].SetPaddingLeft(Notes[j].paddingLeft - Notes[i].paddingNote - Notes[i].WidthAccidental());
                    Notes[j].SetPaddingNote(Notes[j].paddingNote + Notes[i].paddingNote + Notes[i].WidthAccidental());
                }
            }
        }

        // Step 8
        // if any of the notes has a negative padding, move all of them right
        float minP = 0;
        float p;
        for (int i = 0; i < Notes.Count; i++)
        {
            p = Notes[i].paddingLeft;
            if (p < 0 && p < minP) minP = p;
        }
        if (minP < 0)
        {
            for (int i = 0; i < Notes.Count; i++)
            {
                Notes[i].SetPaddingLeft(Notes[i].paddingLeft - minP);
            }
        }

    }

    public override string AsString()
    {
        return "";
    }

    public override float Width()
    {
        int i = 0;
        float w;
        float maxW = 0;
        while (i < Notes.Count)
        {
            w = Notes[i].Width();
            if (w > maxW) maxW = w;
            i++;
        }
        return maxW;
    }

    public override float WidthAccidental()
    {
        return 0f;
    }

    public override float WidthNote()
    {
        return 0f;
    }

    public float WidthAllNoDot()
    {
        return 0f;
    }

    public override float WidthNoteNoStem()
    {
        return 0;
    }

    public override float WidthDot()
    {
        return 0;
    }

    public override void SetDrawParameters(float x, float ys1, float ys2)
    {
        StartX = x;
        StartY1 = ys1;
        StartY2 = ys2;
        for (int i = 0; i < Notes.Count; i++)
        {
            if (Notes[i].stave == 0) Notes[i].SetDrawParameters(StartX, ys1, ys2);
            if (Notes[i].stave == 1) Notes[i].SetDrawParameters(StartX, ys2, ys2);
        }
    }

    public override void DrawNote(ScoreCanvas canvas)
    {
        if (!IsBlurred() && !IsVisible()) return;
        for (int i = 0; i < Notes.Count; i++)
        {
            if (Notes[i].stave == 0) Notes[i].DrawNote(canvas);
            if (Notes[i].stave == 1) Notes[i].DrawNote(canvas);
        }
        base.DrawNote(canvas);
    }

    public override float GetDisplacement()
    {
        return 0;
    }

    public override float Left()
    {
        float left = Notes[0].Left();
        for (int i = 0; i < Notes.Count; i++)
            if (Notes[i].Left() < left) left = Notes[i].Left();
        return left;
    }

    public override float Bottom()
    {
        float bottom = Notes[0].Bottom();
        for (int i = 0; i < Notes.Count; i++)
            if (Notes[i].Bottom() < bottom) bottom = Notes[i].Bottom();
        for (int i = 0; i < Notes.Count; i++)
            if (Notes[i].Top() < bottom) bottom = Notes[i].Top();
        return bottom;
    }

    public override float Right()
    {
        float right = Notes[0].Right();
        for (int i = 0; i < Notes.Count; i++)
            if (Notes[i].Right() > right) right = Notes[i].Right();
        return right;
    }

    public override float Top()
    {
        float top = Notes[0].Top();
        for (int i = 0; i < Notes.Count; i++)
            if (Notes[i].Top() > top) top = Notes[i].Top();
        for (int i = 0; i < Notes.Count; i++)
            if (Notes[i].Bottom() > top) top = Notes[i].Bottom();
        return top;
    }

    public override void SetColor(Color color)
    {
        this.color = color;
        for (int i = 0; i < Notes.Count; i++) Notes[i].SetColor(color);
    }

    public override void SetBlurred(bool blur)
    {
        this.blurred = blur;
        for (int i = 0; i < Notes.Count; i++) Notes[i].SetBlurred(blur);
    }

    public override void SetVisible(bool visible)
    {
        this.visible = visible;
        for (int i = 0; i < Notes.Count; i++) Notes[i].SetVisible(visible);
    }
}
